package com.hb.repository;

import java.util.List;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;

import com.hb.domain.Spittle;

/**
 * Repository interface with operations for {@link Spittle} persistence.
 *
 * @author habuma
 */
public interface SpittleRepository {

    long count();

    @Cacheable("spittleCache")
    List<Spittle> findRecent();

    List<Spittle> findRecent(int count);

    /**
     * @Cacheable 注解被放到了 JdbcSpittleRepository 的 findOne() 方法实现上。这样能够起作用，但是缓存的作用只限于 JdbcSpittleRepository 这个实现类中，
     * SpittleRepository 的其他实现并没有缓存功能，除非也为其添加上 @Cacheable 注解。因此，可以考虑将注解添加到 SpittleRepository 的方法声明上，而不是放在
     * 实现类中。当为接口方法添加注解后，@Cacheable 注解会被 SpittleRepository 的所有实现继承，这些实现类都会应用相同的缓存规则。
     * @param id
     * @return
     */
    // 1、假设对于 message 属性包含 “NoCache” 的 Spittle 对象，我们不想对其进行缓存。为了阻止这样的 Spittle 对象被缓存起来，可以这样设置 unless 属性。
    // 为 unless 设置的 SpEL 表达式会检查返回的 Spittle 对象（在表达式中通过 #result 来识别）的 message 属性。如果它包含 “NoCache” 文本内容，那么这个表达式
    // 的计算值为 true，这个 Spittle 对象不会放进缓存中。否则的话，表达式的计算结果为 false，无法满足 unless 的条件，这个 Spittle 对象会被缓存。
    // 2、属性 unless 能够阻止将值写入到缓存中，但是有时候我们希望将缓存全部禁用。也就是说，在一定的条件下，我们既不希望将值添加到缓存中，也不希望从缓存中获取数据。
    // 例如，对于 ID 值小于 10 的 Spittle 对象，我们不希望对其使用缓存。如果 findOne() 调用时，参数值小于 10，那么将不会在缓存中进行查找，返回的 Spittle 也不
    // 会放进缓存中，就像这个方法没有添加 @Cacheable 注解一样。
    // 如样例所示，unless 属性的表达式能够通过 #result 引用返回值。这是很有用的，这么做之所以可行是因为 unless 属性只有在缓存方法有返回值时才开始发挥作用。而
    // condition 肩负着在方法上禁用缓存的任务，因此它不能等到方法返回时再确定是否该关闭缓存。这意味着它的表达式必须要在进入方法时进行计算，所以我们不能通过
    // #result 引用返回值。
    @Cacheable(value = "spittleCache", unless = "#result.message.contains('NoCache')", condition = "#id >= 10")
    Spittle findOne(long id);

    /**
     * 例如，当一个全新的 Spittle 通过 SpittleRepository 的 save() 方法保存之后，很可能马上就会请求这条记录。所以，当 save() 方法调用后，立即将 Spittle 塞到
     * 缓存之中是很有意义的，这样当其他人通过 findOne() 对其进行查找时，它就已经准备就绪了。为了实现这一点，可以在 save() 方法上添加 @CachePut 注解。
     * 在这里只有一个问题：缓存的 key。如前文所述，默认的缓存 key 要基于方法的参数来确定。因为 save() 方法的唯一参数就是 Spittle，所以它会用作缓存的 key。将 Spittle
     * 放在缓存中，而它的缓存 key 恰好是同一个 Spittle，这是不是有一点诡异呢？显然，在这个场景中，默认的缓存 key 并不是我们想要的。我们需要的缓存 key 是新保存 Spittle
     * 的 ID，而不是 Spittle 本身。所以，在这里需要指定一个 key 而不是使用默认的 key。具体到我们这个场景，我们需要将 key 设置为所保存 Spittle 的 ID。以参数形式传递
     * 给 save() 的 Spittle 还没有保存，因此并没有 ID。我们只能通过 save() 返回的 Spittle 得到 id 属性。表达式 #result 能够得到返回的 Spittle。借助这个对象，我
     * 们可以通过将 key 属性设置为 #result.id 来引用 id 属性。
     * @param spittle
     * @return
     */
    @CachePut(value = "spittleCache", key = "#result.id")
    Spittle save(Spittle spittle);

    @Cacheable("spittleCache")
    List<Spittle> findBySpitterId(long spitterId);

    /**
     * 当 delete() 调用时，会从缓存中删除一个条目。被删除条目的 key 与传递进来的 spittleId 参数的值相等。
     * @param id
     */
    @CacheEvict(value = "spittleCache", condition = "")
    void delete(long id);

}
